import  SimpleXMLRPCServer
import SocketServer
import threading
import sys
import time
import xmlrpclib
from PlusterLog import *

class RPCThreading(SocketServer.ThreadingMixIn, SimpleXMLRPCServer.SimpleXMLRPCServer):
    """Mulit-Thread request handler"""
    def __init__ (self,address):
        """"""
        SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(self,addr=address,logRequests=False,allow_none=True)


class PortDaemon(threading.Thread,PlusterLog):
    """Use a regiested Instance to hanlde requests on a port,every PortDaemon is a Thread,isBind must set true befor start() """

    def __init__(self,addr):
        PlusterLog.__init__(self)
        self.log.info( "Initial PortDaemon at %s :%d"%addr)
        threading.Thread.__init__(self)
        self.port=RPCThreading(addr)
        self.isBind=False  #Must Set True Befor Daemon Start

    def run(self):
        while self.isBind==True:
            self.port.handle_request()

    def exit(self):
        sys.exit()

    def register_instance(self,name):
        self.port.register_instance(name)



class XMLRPC(PlusterLog):
    """XMLRPC service

         Allocate port and bind function or instance . MaxPortNum is
         the number of port which can be used form portBegin to
         portBegin+MaxPortNum.It use PortDaemon to handle each port
         and each PortDaemon is a Multi-Thread request handler.A
         callBack instance is Bind on it"""
    rpcDaemon=0

    def __init__(self,url='localhost',portBegin=8888, MaxPortNum=20):
        #make usre if rpcDaemon already exists
        if XMLRPC.rpcDaemon:
            return
        PlusterLog.__init__(self)
        self.log.info("Initial XML-RPC Service")
        self.pool_size=MaxPortNum
        self.url=url
        self.portDict={}#Dictionary for portNum:portDanmon
        self.portList=[x+portBegin for x in range(MaxPortNum)]#List of allocatable port
        XMLRPC.rpcDaemon=self

    def Stop(self):
        """Force Exit All Daemon ,Invock after all tasks released
        their ports"""
        for daemon in self.portDict.values():
            daemon.exit()
        self.status="Stop"

    def Report(self):
        """Return Pool_size and available port number """
        return self.pool_size,len(self.portList)

    def RequestPort(self,callBackIns):
        """Hooks a callBackInstance on a port
        Return Port Number
        Exception NoPortError"""
        if len(self.portList)>0:
            portNum=self.portList.pop(0)
            try:
                if self.portDict.has_key(portNum):
                    port = self.portDict[portNum]
                    port.isBind=True
                else:
                    port = PortDaemon((self.url,portNum ))
                    self.portDict[portNum]=port
                    port.isBind=True
                    port.setDaemon(True)
                    port.start()
                port.register_instance(callBackIns)
            except Exception,e:
                self.portList.insert(0,portNum)
                raise
            else:
                self.log.info("Using %d port"%portNum)
                return portNum
        else:
            self.log.error("XMLRPC port pool overflow")
            raise Exception,"Use up available Ports Space"


    def ReleasePort(self,portNum):
        """Stop PortDaemon and put portNum into portList"""
        try:
            port=self.portDict[portNum]
        except Exception,e:
            self.log.error(e)
            raise
        else:
             #Set this flag will stop this portDaemon thread
            #port.isBind=False
            self.portList.insert(0,portNum)
            self.log.info("Release port %d"%portNum)

